<!DOCTYPE html><html lang="zh-Hans"><head><meta name="generator" content="Hexo 3.8.0"><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0" name="viewport"><meta content="yes" name="apple-mobile-web-app-capable"><meta content="black-translucent" name="apple-mobile-web-app-status-bar-style"><meta content="telephone=no" name="format-detection"><meta name="description" content="漫漫IT路，持一颗恒心，带一份执着！"><title>菜鸟IT路 | dev-heaven</title><link rel="stylesheet" type="text/css" href="/css/style.css?v=0.0.0"><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/normalize/8.0.1/normalize.min.css"><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/pure/1.0.0/pure-min.css"><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/pure/1.0.0/grids-responsive-min.css"><link rel="stylesheet" href="//lib.baomitu.com/font-awesome/4.7.0/css/font-awesome.min.css"><script type="text/javascript" src="//lib.baomitu.com/jquery/3.4.0/jquery.min.js"></script><link rel="icon" mask sizes="any" href="/favicon.ico"><link rel="Shortcut Icon" type="image/x-icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="apple-touch-icon-precomposed" href="/apple-touch-icon.png"></head><body><div class="body_container"><div id="header"><div class="site-name"><h1 class="hidden">菜鸟IT路</h1><a id="logo" href="/.">菜鸟IT路</a><p class="description">dev-heaven</p></div><div id="nav-menu"><a class="current" href="/."><i class="fa fa-home">Inicio</i></a><a href="/archives/"><i class="fa fa-archive"> Archivo</i></a><a href="/about/"><i class="fa fa-user"> Acerca de</i></a><a href="/atom.xml"><i class="fa fa-rss"> RSS</i></a></div></div><div class="pure-g" id="layout"><div class="pure-u-1 pure-u-md-3-4"><div class="content_container"><div class="post"><h1 class="post-title"><a href="/posts/48761.html">关于ReentrantLock实现的一点思考</a></h1><div class="post-meta">2019-01-16</div><div class="post-content"><p>ReentrantLock内部由Sync类实现，Sync类是ReentrantLock的一个内部类，该类继承自AbstractQueuedSynchronizer，该类继承自AbstractOwnableSynchronizer，AbstractOwnableSynchronizer中持有一个exclusiveOwnerThread，表示当前持有锁的线程。</p><p>Sync类是一个抽象类，它有两个实现类：FairSync和NonFairSync，分别代表公平锁和非公平锁。</p><p>非公平锁和公平锁实现类似，前者相对简单，在AbstractQueuedSynchronizer中有一个整型state变量，在Java中通过Unsafe类提供的CAS来操作state变量，用于表示锁是否获取（0表示当前没有线程获取锁，反之非0）</p><p>在非公平锁中，首先尝试cas(state)，如果成功，则表示当前线程获取锁，此时将exclusiveOwnerThread设置为当前线程，否则会判断state是否为0，如果是，则再次cas(state)，否则判断当前锁的持有者是否是当前线程，如果是，则state加一操作，否则获取锁失败。然后将当前线程加入等待队列，然后以自旋的方式等待锁。</p><p>公平锁和非公平锁实现过程差别不大，差别在于，如果是非公平锁的话，当某个线程调用lock方法时，它不会抢占式的去设置同步状态（即state的值），而不是直接调用 acquire 将线程放入同步队列中等待获取锁，当抢占失败，才会放入队列中等待锁。除此之外，在调用tryAcquire时，公平锁每次回判断是否有线程比当前线程等待时间更长，如果有，则让队列中第一个线程获取锁。</p><p>参考：<br>— <a href="https://www.cnblogs.com/xrq730/p/4979021.html" target="_blank" rel="noopener">https://www.cnblogs.com/xrq730/p/4979021.html</a></p><p>— <a href="http://www.cnblogs.com/nullllun/p/9004309.html" target="_blank" rel="noopener">http://www.cnblogs.com/nullllun/p/9004309.html</a></p></div><p class="readmore"><a href="/posts/48761.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/61735.html">Java内存优化之POI Excel（三）</a></h1><div class="post-meta">2018-06-26</div><div class="post-content"><blockquote><p>在上一篇工作记录中提到了<a href="http://www.jianshu.com/p/8c640c21e487" target="_blank" rel="noopener">Excel内存优化策略</a>，其实很失败。很多东西好像自己以为想当然，其实不是。以为我将Excel分成5000行一个文件，既可以优化内存，又可以方便阅读，直到有一天我看到结算组是怎么使用呢？打开每个Excel，复制粘贴到一个文件里，当看到他们打开一个Zip，里面有11个Excel，我都不忍直视了……其实做这种优化比较耗时间，效果也不是很容易看到，可能做了好几天这套方案，结果并没有收益，一转眼又要周五了，还好这次有效果。</p></blockquote><h3 id="让过程更直观"><a href="#让过程更直观" class="headerlink" title="让过程更直观"></a>让过程更直观</h3><p>上篇博客提到了JConsole，可以让我们更加直观的看到任务执行过程中的各种信息，比如内存（Eden、Old、Survivor），线程数，gc过程和次数等。如下</p><p>大概截取了一个任务执行的所有过程，可以看出系统初始化内存基本上稳定在200-300m，然后猛然增加了200+m，这就是从数据库撸出来的近50000条数据，</p></div><p class="readmore"><a href="/posts/61735.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/64862.html">Java内存优化之POI Excel（二）</a></h1><div class="post-meta">2018-06-25</div><div class="post-content"><blockquote><p>问题背景及排查：<a href="http://www.jianshu.com/p/787192569d03" target="_blank" rel="noopener">Java内存优化之POI Excel（一）</a><br>其实除了内存调整，还做了策略上的调整，既然创建一整个数十万行的Excel比较耗内存，那我就分片创建呗，所谓分片就是将一个大的Excel分成几个小的Excel，然后打成Zip。</p></blockquote><h4 id="分片创建Excel"><a href="#分片创建Excel" class="headerlink" title="分片创建Excel"></a>分片创建Excel</h4><blockquote><p>Talk is cheap, show me the code!<br></p><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">String</span> export() <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        List&lt;<span class="keyword">String</span>&gt; excelFileSplices = Lists.newArrayList();</span><br><span class="line">        <span class="keyword">if</span> (pageable) &#123;</span><br><span class="line">            <span class="comment">// 可以分成几个Excel</span></span><br><span class="line">            <span class="built_in">int</span> pageCount = <span class="keyword">this</span>.dataList.<span class="built_in">size</span>() / PER_SPLICE_SIZE + <span class="number">1</span>;</span><br><span class="line">            <span class="built_in">int</span> index = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">while</span> (index &lt; pageCount) &#123;</span><br><span class="line">                List&lt;T&gt; dataSplices = <span class="keyword">this</span>.dataList.subList(index * PER_SPLICE_SIZE,</span><br><span class="line">                        Math.<span class="built_in">min</span>(<span class="keyword">this</span>.dataList.<span class="built_in">size</span>(), (index + <span class="number">1</span>) * PER_SPLICE_SIZE));</span><br><span class="line">                <span class="keyword">String</span> fileName = exportOne(dataSplices);</span><br><span class="line">                excelFileSplices.<span class="built_in">add</span>(fileName);</span><br><span class="line">                index++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">String</span> fileName = exportOne(<span class="keyword">this</span>.dataList, ignoredProperties);</span><br><span class="line">            excelFileSplices.<span class="built_in">add</span>(fileName);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">log</span>.info(excelFileSplices);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (excelFileSplices.<span class="built_in">size</span>() == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> excelFileSplices.<span class="built_in">get</span>(<span class="number">0</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">String</span> zipFilePath = TMP_FILE + <span class="string">"/"</span> + UUID.randomUUID().toString() + <span class="string">".zip"</span>;</span><br><span class="line">            ZipArchiveOutputStream zipArchiveOutputStream = <span class="keyword">null</span>;</span><br><span class="line">            InputStream is = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                zipArchiveOutputStream = <span class="keyword">new</span> ZipArchiveOutputStream(<span class="keyword">new</span> File(zipFilePath));</span><br><span class="line">                zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">String</span> srcFilePath : excelFileSplices) &#123;</span><br><span class="line">                    File sourceFile = <span class="keyword">new</span> File(srcFilePath);</span><br><span class="line">                    ZipArchiveEntry zipArchiveEntry = <span class="keyword">new</span> ZipArchiveEntry(sourceFile, sourceFile.getName());</span><br><span class="line">                    zipArchiveOutputStream.putArchiveEntry(zipArchiveEntry);</span><br><span class="line">                    is = <span class="keyword">new</span> BufferedInputStream(<span class="keyword">new</span> FileInputStream(sourceFile));</span><br><span class="line">                    IOUtils.<span class="built_in">copy</span>(is, zipArchiveOutputStream);</span><br><span class="line">                    sourceFile.delete();</span><br><span class="line">                &#125;</span><br><span class="line">                zipArchiveOutputStream.closeArchiveEntry();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                IOUtils.closeQuietly(is);</span><br><span class="line">                IOUtils.closeQuietly(zipArchiveOutputStream);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> zipFilePath;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p></p></blockquote><p>其实策略实现比较简单，就是将数据全部撸到内存（其实这些数据也占不了多少内存，只是创建Excel的时候消耗了大量内存），然后在内存分页，分别创建Excel，最后将Excel达成Zip包上传到七牛。</p><h4 id="不妥协的DaYe"><a href="#不妥协的DaYe" class="headerlink" title="不妥协的DaYe"></a>不妥协的DaYe</h4><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">WO: 以后下载清单会是压缩包了，会麻烦一点点<span class="built_in">..</span>.</span><br><span class="line">DaYe：我们要这个就是为了简单吗 ，你还麻烦一点 ，你怎么好意思说啊<span class="built_in">..</span>.</span><br><span class="line">WO: <span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span></span><br></pre></td></tr></table></figure><p>不说了，继续改吧。</p><h3 id="可见的效果"><a href="#可见的效果" class="headerlink" title="可见的效果"></a>可见的效果</h3><p>其实这种性能优化，最讨厌感觉上优化了，明明做了啊，但是到底优化了多少，是一点点，还是很多，还是适得其反呢？怎么能做到心中没点B数。</p><h4 id="神奇的JConsole"><a href="#神奇的JConsole" class="headerlink" title="神奇的JConsole"></a>神奇的JConsole</h4><p>Sun真是良心，除了JDK，还提供了很多很好的附加工具，还说只是试验品，软件附加的，但是其实很好用，而且很稳定。</p><p>什么是JConsole呢？在终端（Mac或者Windows）上输个<code>jconsole</code>就能看到，如图</p><p><img src="http://upload-images.jianshu.io/upload_images/257478-985132fb800b4c39.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>图中<code>com.zuihuibao.excel.ExcelOperator 34890</code>是我启动的程序，可以选中它，然后连接。</p><p><img src="http://upload-images.jianshu.io/upload_images/257478-8945219a2017dd56.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>点击<code>不安全的连接</code>，就可以看到各种你想看到的数据，比如<code>当前内存大小</code>，<code>内存峰值</code>等，我打算用它来测量我优化的实际效果。</p><p><img src="http://upload-images.jianshu.io/upload_images/257478-eded2a27b1ffd5ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><blockquote><p>PS，可能第一次你一直连不上，请在你的启动程序上加个参数<br><code>-Djava.rmi.server.hostname=localhost</code> <img src="http://upload-images.jianshu.io/upload_images/257478-8ad151d36234d10c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p></blockquote></div><p class="readmore"><a href="/posts/64862.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/27940.html">Java内存优化之POI Excel（一）</a></h1><div class="post-meta">2018-06-22</div><div class="post-content"><blockquote><p>结算系统上线后，每到月初月末，都有点胆战心惊，最怕听到“某某某，我这个下载又不行”、“我这个都下载了20分钟了，怎么还不行啊！”…… 我能怎么办哇，停下来把锅捡起来呗。</p></blockquote><h3 id="捡锅记之检锅"><a href="#捡锅记之检锅" class="headerlink" title="捡锅记之检锅"></a>捡锅记之检锅</h3><p>捡锅了，然后呢？当然是查一查问题出在哪了。ssh上服务器，先说说服务器配置吧。这台服务器是在某离职大神的建议下购买的，配置还不错。<code>单核SSD硬盘</code>，其他配置如下：<br></p><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[web@monitor ~]$ free -m</span><br><span class="line">              total        used        free      shared  buff/cache   available</span><br><span class="line">Mem:          <span class="number"> 3790 </span>      <span class="number"> 2467 </span>       <span class="number"> 173 </span>         <span class="number"> 0 </span>      <span class="number"> 1150 </span>       1021</span><br><span class="line">Swap:            <span class="number"> 0 </span>         <span class="number"> 0 </span>          0</span><br><span class="line"></span><br><span class="line">[web@monitor ~]$ cat /etc/redhat-release </span><br><span class="line">CentOS Linux release 7.3.1611 (Core)</span><br></pre></td></tr></table></figure><p></p><h4 id="查看服务状态"><a href="#查看服务状态" class="headerlink" title="查看服务状态"></a>查看服务状态</h4><p>记得当初车险系统刚升级SpringBoot的时候，经常发现系统挂掉，Java进程也被kill掉，还纠结了好久，排查了很久发现是被由于Linux的OOM Killer机制杀掉的。</p><blockquote><p>Linux OOM_killer是Linux自我保护的方式，当内存不足时不至于出现太严重问题，有点壮士断腕的意味。在kernel 2.6，内存不足将唤醒oom_killer，挑出/proc/<pid>/oom_score最大者并将之kill掉，可以把/proc/<pid>/oom_score_adj值改小（最小-17）来临时避免此种情况。</pid></pid></p></blockquote><p>所以出现服务停止后，我第一反应就是查看Java进程还在不在。<br></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[web@monitor ~]$ jcmd</span><br><span class="line"><span class="number">19335</span> settlement<span class="selector-class">.jar</span> --spring<span class="selector-class">.profiles</span><span class="selector-class">.active</span>=prod</span><br><span class="line"><span class="number">30142</span> sun<span class="selector-class">.tools</span><span class="selector-class">.jcmd</span><span class="selector-class">.JCmd</span></span><br></pre></td></tr></table></figure><p></p><p>居然还在，唯一的借口都不给我！</p><h4 id="检查Java进程栈"><a href="#检查Java进程栈" class="headerlink" title="检查Java进程栈"></a>检查Java进程栈</h4><p>我第一个想到的是死锁，导致进程假死。看看Java栈：<br></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">[web@monitor ~]$ jstack -l <span class="number">19335</span></span><br><span class="line"><span class="number">2017</span>-<span class="number">11</span>-<span class="number">03</span> <span class="number">21</span>:<span class="number">44</span>:<span class="number">07</span></span><br><span class="line">Full thread dump Java HotSpot(TM) <span class="number">64</span>-Bit Server VM (<span class="number">25.144</span>-b01 mixed mode):</span><br><span class="line"></span><br><span class="line"><span class="string">"http-nio-8087-exec-10"</span> <span class="number">#101</span>7 daemon prio=<span class="number">5</span> os_prio=<span class="number">0</span> tid=<span class="number">0</span>x00007f2cd0016000 nid=<span class="number">0</span>x5c31 waiting on condition [<span class="number">0</span>x00007f2c9d21a000]</span><br><span class="line">   java<span class="selector-class">.lang</span><span class="selector-class">.Thread</span><span class="selector-class">.State</span>: WAITING (parking)</span><br><span class="line">	at sun<span class="selector-class">.misc</span><span class="selector-class">.Unsafe</span><span class="selector-class">.park</span>(Native Method)</span><br><span class="line">	- parking to wait <span class="keyword">for</span>  &lt;<span class="number">0</span>x00000000920abcf8&gt; (<span class="selector-tag">a</span> java<span class="selector-class">.util</span><span class="selector-class">.concurrent</span><span class="selector-class">.locks</span><span class="selector-class">.AbstractQueuedSynchronizer</span><span class="variable">$ConditionObject</span>)</span><br><span class="line">	at java<span class="selector-class">.util</span><span class="selector-class">.concurrent</span><span class="selector-class">.locks</span><span class="selector-class">.LockSupport</span><span class="selector-class">.park</span>(LockSupport<span class="selector-class">.java</span>:<span class="number">175</span>)</span><br><span class="line">	at java<span class="selector-class">.util</span><span class="selector-class">.concurrent</span><span class="selector-class">.locks</span><span class="selector-class">.AbstractQueuedSynchronizer</span><span class="variable">$ConditionObject</span>.await(AbstractQueuedSynchronizer<span class="selector-class">.java</span>:<span class="number">2039</span>)</span><br><span class="line">	at java<span class="selector-class">.util</span><span class="selector-class">.concurrent</span><span class="selector-class">.LinkedBlockingQueue</span><span class="selector-class">.take</span>(LinkedBlockingQueue<span class="selector-class">.java</span>:<span class="number">442</span>)</span><br><span class="line">	at org<span class="selector-class">.apache</span><span class="selector-class">.tomcat</span><span class="selector-class">.util</span><span class="selector-class">.threads</span><span class="selector-class">.TaskQueue</span><span class="selector-class">.take</span>(TaskQueue<span class="selector-class">.java</span>:<span class="number">103</span>)</span><br><span class="line">	at org<span class="selector-class">.apache</span><span class="selector-class">.tomcat</span><span class="selector-class">.util</span><span class="selector-class">.threads</span><span class="selector-class">.TaskQueue</span><span class="selector-class">.take</span>(TaskQueue<span class="selector-class">.java</span>:<span class="number">31</span>)</span><br><span class="line">	at java<span class="selector-class">.util</span><span class="selector-class">.concurrent</span><span class="selector-class">.ThreadPoolExecutor</span><span class="selector-class">.getTask</span>(ThreadPoolExecutor<span class="selector-class">.java</span>:<span class="number">1074</span>)</span><br><span class="line">	at java<span class="selector-class">.util</span><span class="selector-class">.concurrent</span><span class="selector-class">.ThreadPoolExecutor</span><span class="selector-class">.runWorker</span>(ThreadPoolExecutor<span class="selector-class">.java</span>:<span class="number">1134</span>)</span><br><span class="line">	at java<span class="selector-class">.util</span><span class="selector-class">.concurrent</span><span class="selector-class">.ThreadPoolExecutor</span><span class="variable">$Worker</span>.run(ThreadPoolExecutor<span class="selector-class">.java</span>:<span class="number">624</span>)</span><br><span class="line">	at org<span class="selector-class">.apache</span><span class="selector-class">.tomcat</span><span class="selector-class">.util</span><span class="selector-class">.threads</span><span class="selector-class">.TaskThread</span><span class="variable">$WrappingRunnable</span>.run(TaskThread<span class="selector-class">.java</span>:<span class="number">61</span>)</span><br><span class="line">	at java<span class="selector-class">.lang</span><span class="selector-class">.Thread</span><span class="selector-class">.run</span>(Thread<span class="selector-class">.java</span>:<span class="number">748</span>)</span><br><span class="line"></span><br><span class="line">   Locked ownable synchronizers:</span><br><span class="line">	- None</span><br><span class="line">.....</span><br></pre></td></tr></table></figure><p></p><p>太长了，此处截取部分。我记得jstack 有时候可以帮我们检测出死锁的，可以直接<br></p><figure class="highlight gherkin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jstack -l <span class="variable">&lt;pid&gt;</span> |<span class="string"> grep deadlock</span></span><br></pre></td></tr></table></figure><p></p><p>如果有结果，则表示有死锁，当然也不排除其他情况（ps: 也遇到过数据库链接失效，导致某任务执行了3个多小时后失败的。）jstack的其他情况可以参考下：<a href="http://blog.csdn.net/wanglha/article/details/51133819" target="_blank" rel="noopener">http://blog.csdn.net/wanglha/article/details/51133819</a></p><h4 id="检查Java堆"><a href="#检查Java堆" class="headerlink" title="检查Java堆"></a>检查Java堆</h4><p>Java对象大部分情况实在堆上创建的（有时候会在栈上或者有块叫做TLAB的空间），那么来检查下堆的情况吧。<br></p><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">[web@monitor ~]$ jmap -heap <span class="number">19335</span></span><br><span class="line">Attaching to process ID <span class="number">19335</span>, please wait...</span><br><span class="line">Debugger attached successfully.</span><br><span class="line">Server compiler detected.</span><br><span class="line">JVM version is <span class="number">25.144</span>-b01</span><br><span class="line"></span><br><span class="line">using thread-local object allocation.</span><br><span class="line">Parallel GC <span class="keyword">with</span> <span class="number">2</span> thread(s)</span><br><span class="line"></span><br><span class="line">Heap Configuration:</span><br><span class="line">   <span class="attr">MinHeapFreeRatio</span>         = <span class="number">0</span></span><br><span class="line">   <span class="attr">MaxHeapFreeRatio</span>         = <span class="number">100</span></span><br><span class="line">   <span class="attr">MaxHeapSize</span>              = <span class="number">1879048192</span> (<span class="number">1792.0</span>MB)</span><br><span class="line">   <span class="attr">NewSize</span>                  = <span class="number">625999872</span> (<span class="number">597.0</span>MB)</span><br><span class="line">   <span class="attr">MaxNewSize</span>               = <span class="number">625999872</span> (<span class="number">597.0</span>MB)</span><br><span class="line">   <span class="attr">OldSize</span>                  = <span class="number">1253048320</span> (<span class="number">1195.0</span>MB)</span><br><span class="line">   <span class="attr">NewRatio</span>                 = <span class="number">2</span></span><br><span class="line">   <span class="attr">SurvivorRatio</span>            = <span class="number">5</span></span><br><span class="line">   <span class="attr">MetaspaceSize</span>            = <span class="number">21807104</span> (<span class="number">20.796875</span>MB)</span><br><span class="line">   <span class="attr">CompressedClassSpaceSize</span> = <span class="number">1073741824</span> (<span class="number">1024.0</span>MB)</span><br><span class="line">   <span class="attr">MaxMetaspaceSize</span>         = <span class="number">17592186044415</span> MB</span><br><span class="line">   <span class="attr">G1HeapRegionSize</span>         = <span class="number">0</span> (<span class="number">0.0</span>MB)</span><br><span class="line"></span><br><span class="line">Heap Usage:</span><br><span class="line">PS Young Generation</span><br><span class="line">Eden Space:</span><br><span class="line">   <span class="attr">capacity</span> = <span class="number">569901056</span> (<span class="number">543.5</span>MB)</span><br><span class="line">   <span class="attr">used</span>     = <span class="number">105560864</span> (<span class="number">543.5</span>MB)</span><br><span class="line">   <span class="attr">free</span>     = <span class="number">464340192</span> (<span class="number">0</span>MB)</span><br><span class="line">   <span class="number">100</span>% used</span><br><span class="line">From Space:</span><br><span class="line">   <span class="attr">capacity</span> = <span class="number">27262976</span> (<span class="number">26.0</span>MB)</span><br><span class="line">   <span class="attr">used</span>     = <span class="number">6621032</span> (<span class="number">6.314308166503906</span>MB)</span><br><span class="line">   <span class="attr">free</span>     = <span class="number">20641944</span> (<span class="number">19.685691833496094</span>MB)</span><br><span class="line">   <span class="number">24.28580064039964</span>% used</span><br><span class="line">To Space:</span><br><span class="line">   <span class="attr">capacity</span> = <span class="number">26214400</span> (<span class="number">25.0</span>MB)</span><br><span class="line">   <span class="attr">used</span>     = <span class="number">0</span> (<span class="number">0.0</span>MB)</span><br><span class="line">   <span class="attr">free</span>     = <span class="number">26214400</span> (<span class="number">25.0</span>MB)</span><br><span class="line">   <span class="number">0.0</span>% used</span><br><span class="line">PS Old Generation</span><br><span class="line">   <span class="attr">capacity</span> = <span class="number">1253048320</span> (<span class="number">1195.0</span>MB)</span><br><span class="line">   <span class="attr">used</span>     = <span class="number">55013328</span> (<span class="number">1195.0</span>MB)</span><br><span class="line">   <span class="attr">free</span>     = <span class="number">1198034992</span> (<span class="number">0</span>MB)</span><br><span class="line">  <span class="number">100</span>% used</span><br></pre></td></tr></table></figure><p></p><p>纳尼！！！Eden区和老年代都耗尽了（这些数据是后来假造的，当时的确看到的是两个100%）。</p><h4 id="看看gc情况"><a href="#看看gc情况" class="headerlink" title="看看gc情况"></a>看看gc情况</h4><figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[web@monitor ~]$ jstat -gcutil <span class="number">19335</span></span><br><span class="line">  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   </span><br><span class="line">  <span class="number">0.00</span>  <span class="number">99.79</span>  <span class="number">99.89</span>   <span class="number">98.99</span>  <span class="number">97.96</span>  <span class="number">96.07</span>    <span class="number">901</span>    <span class="number">3.654</span>     <span class="number">287</span>    <span class="number">1.160</span>    <span class="number">4.814</span></span><br></pre></td></tr></table></figure><blockquote><p>介绍下这几个参数，S0、S1表示两个Survivor区（复制GC算法使用），E表示Eden区，大部分对象在此创建，O老年代，多次GC后对象存活区域，M表示元数据区（JAVA8后取代永久代，存放一些class元数据，我们不用太关心它，基本上有JVM帮我们管理），YGC和FGC分别表示Eden区GC次数和Full GC的次数，正常情况下Full GC次数比较少，跑个几天Full GC几次差不多了，如果很多次就不正常了（当然排除哪个二货手动调用<code>System.gc()</code>），Full GC比较多，你可能会看到如下错误：<code>java.lang.OutOfMemoryError: GC overhead limit exceeded</code>，这个表示<code>JVM试图通过GC回收内存，但是什么也没有回收到。默认情况下，JVM花费了98%的时间在GC上，但是GC过之后只有不到2%的堆内存被回收。</code></p></blockquote><h3 id="甩锅记之内存的锅"><a href="#甩锅记之内存的锅" class="headerlink" title="甩锅记之内存的锅"></a>甩锅记之内存的锅</h3><p>我去看下这群货到底要下载什么数据，一看吓一跳，两个月全国所有的保单数据。这是什么概念呢？两个月差不多20多万数据吧，如果不做关联单，那差不多是30多万到40万左右，30万行的Excel什么概念？自己想想吧。</p><p>我当然不能说你们别下这么多数据啊，反正我一个打工小弟说了也没人听啊！好吧，升级内存总可以吧，这锅你背。<br></p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">java </span>-Xms1536m -Xmx1536m -<span class="keyword">jar </span>service.<span class="keyword">jar</span></span><br></pre></td></tr></table></figure><p></p><blockquote><p>初始化内存和最大内存设置一样是为了减少GC次数，为啥要减少GC呢？简单说就是GC比较霸道，它工作的时候<code>STOP THE WORLD</code>，所有的工作都得停下，等待GC完成。话说什么时候开始GC呢？你可以使用如下命令观察下：<br></p><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"> jstat -gcutil &lt;pid&gt; <span class="string">[intervalTime]</span> <span class="string">[invokeCount]</span></span><br><span class="line">其中intervalTime表示多久执行一次（单位毫秒），invokeCount表示总共会执行多少次。如：jstat -gcutil <span class="number">19335</span> <span class="number">1000</span></span><br></pre></td></tr></table></figure><p></p><p>大概当Eden区打到100%时会发生一次Young GC，Full GC类似。</p></blockquote><h4 id="堆内存的各个区"><a href="#堆内存的各个区" class="headerlink" title="堆内存的各个区"></a>堆内存的各个区</h4><blockquote><p>改完内存继续观察（后面还会升内存，毕竟这锅给内存背比较简单🤣），发现其实Eden、Survivor、Old区使用情况有很大差异，有的区很快就满了，有的还不到才20几。不行啊，得改，平均点！（这里说的是使用情况，不是都一样大）。</p></blockquote><h5 id="各个区大小比例"><a href="#各个区大小比例" class="headerlink" title="各个区大小比例"></a>各个区大小比例</h5><p>可以通过jmap命令查看<br></p><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">[jarvan4dev@Macbook] ~ $ jmap -heap <span class="number">34890</span></span><br><span class="line">Attaching to process ID <span class="number">34890</span>, please wait...</span><br><span class="line">Debugger attached successfully.</span><br><span class="line">Server compiler detected.</span><br><span class="line">JVM version is <span class="number">25.74</span>-b02</span><br><span class="line"></span><br><span class="line">using thread-local object allocation.</span><br><span class="line">Parallel GC <span class="keyword">with</span> <span class="number">4</span> thread(s)</span><br><span class="line"></span><br><span class="line">Heap Configuration:</span><br><span class="line">   <span class="attr">MinHeapFreeRatio</span>         = <span class="number">0</span></span><br><span class="line">   <span class="attr">MaxHeapFreeRatio</span>         = <span class="number">100</span></span><br><span class="line">   <span class="attr">MaxHeapSize</span>              = <span class="number">2147483648</span> (<span class="number">2048.0</span>MB)</span><br><span class="line">   <span class="attr">NewSize</span>                  = <span class="number">44564480</span> (<span class="number">42.5</span>MB)</span><br><span class="line">   <span class="attr">MaxNewSize</span>               = <span class="number">715653120</span> (<span class="number">682.5</span>MB)</span><br><span class="line">   <span class="attr">OldSize</span>                  = <span class="number">89653248</span> (<span class="number">85.5</span>MB)</span><br><span class="line">   <span class="attr">NewRatio</span>                 = <span class="number">2</span></span><br><span class="line">   <span class="attr">SurvivorRatio</span>            = <span class="number">8</span></span><br><span class="line">   <span class="attr">MetaspaceSize</span>            = <span class="number">21807104</span> (<span class="number">20.796875</span>MB)</span><br><span class="line">   <span class="attr">CompressedClassSpaceSize</span> = <span class="number">1073741824</span> (<span class="number">1024.0</span>MB)</span><br><span class="line">   <span class="attr">MaxMetaspaceSize</span>         = <span class="number">17592186044415</span> MB</span><br><span class="line">   <span class="attr">G1HeapRegionSize</span>         = <span class="number">0</span> (<span class="number">0.0</span>MB)</span><br></pre></td></tr></table></figure><p></p><p>默认情况下（JDK8，不同版本可能不一样），NewRatio=2，表示新生代和老年代比例为1：2，SurvivorRatio=8，表示<strong>一个</strong>Survivor区和新生代比例为1：8，两个Survivor区（S0和S1），即每个Survivor区占堆内存的1/10，调节这两个参数可以调节各区大小比例，来达到最优的使用情况。<br></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -Xms1792m -XmX1792m -XX:<span class="attribute">NewRatio</span>=2 -XX:<span class="attribute">SurvivorRatio</span>=5 -jar service.jar</span><br></pre></td></tr></table></figure><p></p></div><p class="readmore"><a href="/posts/27940.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/955.html">最佳实践之Java基础</a></h1><div class="post-meta">2017-12-28</div><div class="post-content"><h4 id="List转String"><a href="#List转String" class="headerlink" title="List转String[]"></a>List<javabean>转String[]</javabean></h4><blockquote><p>在开发中可能会遇到Javabean链表转String数组，举个栗子<br>有一个List<user>获取所有的username，以数组方式</user></p></blockquote><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">class<span class="built_in"> User </span>&#123;</span><br><span class="line">	private String username</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">List&lt;User&gt;<span class="built_in"> users </span>= getUserList();</span><br><span class="line">List&lt;String&gt; usernameList = users.stream().map(User::getUsername)</span><br><span class="line">					.collect(Collectors.toList());</span><br><span class="line">String[] usernames = org.apache.commons.lang3.ArrayUtils.toStringArray(usernameList.toArray());</span><br></pre></td></tr></table></figure></div><p class="readmore"><a href="/posts/955.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/49259.html">Vue + SpringBoot 小问题记录</a></h1><div class="post-meta">2017-12-26</div><div class="post-content"><blockquote><p>下午事情不是特别多的时候，闲下来看看spring security，配合着<a href="https://github.com/iview/iview-admin" target="_blank" rel="noopener">iview-admin</a>搞了个小系统，过段时间释放出源码来。当前在登录这块遇到点小问题，记录一下。</p></blockquote><p>先介绍下这个系统“架构”，前端Vue，后端springboot，前后端分离，登录的时候将后端生成的token放入header中，key为Authorization，然后每次请求的时候带上以供服务器验证。</p><h3 id="零、跨域问题"><a href="#零、跨域问题" class="headerlink" title="零、跨域问题"></a>零、跨域问题</h3><p>跨域是什么就不介绍了，前后端分离肯定要考虑跨域问题，跨域也有多种解决方案，比较常用的两种：一种是在服务端允许部分或者全部Origin，另一种是在前端配置，前端配置又分成两种方案，注意这里之所以说前端也有两种方案是因为我前端打包工具用的是webpack，webpack里面有个组件proxyTable，关于<a href="https://www.jianshu.com/p/95b2caf7e0da" target="_blank" rel="noopener">proxyTable</a>就不细说了，请自行谷百，毕竟我是个后端汪。注意proxyTable是在本地调试的时候使用的哦！前端的另一种配置是在Nginx，这种一般在生产中使用。</p><h4 id="1-Nginx跨域配置"><a href="#1-Nginx跨域配置" class="headerlink" title="1. Nginx跨域配置"></a>1. Nginx跨域配置</h4><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">location</span> /api/ &#123;</span><br><span class="line">       <span class="attribute">set</span> <span class="variable">$allow_headers</span> <span class="string">'Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'</span>;</span><br><span class="line">       <span class="attribute">set</span> <span class="variable">$allow_method</span> <span class="string">'GET, POST, PUT, DELETE, OPTIONS'</span>;</span><br><span class="line">       <span class="attribute">if</span> (<span class="variable">$request_method</span> = <span class="string">'OPTIONS'</span>) &#123;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Origin'</span> <span class="variable">$http_origin</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Credentials'</span> <span class="string">'true'</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Methods'</span> <span class="variable">$allow_method</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Headers'</span> <span class="variable">$allow_headers</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Max-Age'</span> <span class="number">1728000</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Content-Type'</span> <span class="string">'text/plain charset=UTF-8'</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Content-Length'</span> <span class="number">0</span>;</span><br><span class="line">           <span class="attribute">return</span> <span class="number">204</span>;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="attribute">if</span> (<span class="variable">$request_method</span> = <span class="string">'GET'</span>) &#123;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Origin'</span> <span class="variable">$http_origin</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Credentials'</span> <span class="string">'true'</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Methods'</span> <span class="variable">$request_method</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Headers'</span> <span class="variable">$allow_headers</span>;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="attribute">if</span> (<span class="variable">$request_method</span> = <span class="string">'POST'</span>) &#123;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Origin'</span> <span class="variable">$http_origin</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Credentials'</span> <span class="string">'true'</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Methods'</span> <span class="variable">$request_method</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Headers'</span> <span class="variable">$allow_headers</span>;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="attribute">if</span> (<span class="variable">$request_method</span> = <span class="string">'PUT'</span>) &#123;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Origin'</span> <span class="variable">$http_origin</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Credentials'</span> <span class="string">'true'</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Methods'</span> <span class="variable">$request_method</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Headers'</span> <span class="variable">$allow_headers</span>;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="attribute">if</span> (<span class="variable">$request_method</span> = <span class="string">'DELETE'</span>) &#123;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Origin'</span> <span class="variable">$http_origin</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Credentials'</span> <span class="string">'true'</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Methods'</span> <span class="variable">$request_method</span>;</span><br><span class="line">           <span class="attribute">add_header</span> <span class="string">'Access-Control-Allow-Headers'</span> <span class="variable">$allow_headers</span>;</span><br><span class="line">       &#125;</span><br><span class="line"><span class="attribute">port_in_redirect</span> <span class="literal">on</span>;</span><br><span class="line">   <span class="attribute">proxy_pass</span> http://127.0.0.1:8080/your_context_path/;</span><br><span class="line">   <span class="attribute">proxy_redirect</span>  <span class="literal">off</span>;</span><br><span class="line"><span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line"><span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line"><span class="attribute">proxy_set_header</span> X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="2-SpringBoot对跨域的支持"><a href="#2-SpringBoot对跨域的支持" class="headerlink" title="2. SpringBoot对跨域的支持"></a>2. SpringBoot对跨域的支持</h4><blockquote><p>作为后端汪，为了图方便我肯定选择在后端配置支持跨域，熟悉也简单。</p></blockquote><p>只需要声明一个bean即可</p><figure class="highlight aspectj"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> <span class="function">WebMvcConfigurer <span class="title">corsConfigurer</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> WebMvcConfigurerAdapter() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">addCorsMappings</span><span class="params">(CorsRegistry registry)</span> </span>&#123;</span><br><span class="line">            registry.addMapping(<span class="string">"/**"</span>)</span><br><span class="line">                    .allowedHeaders(<span class="string">"*"</span>)</span><br><span class="line">                    .allowedOrigins(<span class="string">"*"</span>).allowedMethods(<span class="string">"*"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以查看allowedXXX()的实现，Java Doc上有很明确的说明，”*”表示支持所有。</p><h4 id="3-Spring-security对跨域的支持"><a href="#3-Spring-security对跨域的支持" class="headerlink" title="3. Spring security对跨域的支持"></a>3. Spring security对跨域的支持</h4><blockquote><p>为什么还要配置security对跨域的支持呢？因为我的登录是是交给Spring security完成的。</p></blockquote><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Override</span></span><br><span class="line">protected void configure(HttpSecurity http) throws Exception &#123;</span><br><span class="line">    <span class="selector-tag">http</span></span><br><span class="line">            <span class="comment">// 由于使用的是JWT，我们这里不需要csrf</span></span><br><span class="line">            <span class="selector-class">.csrf</span>()<span class="selector-class">.disable</span>()</span><br><span class="line">            <span class="selector-class">.cors</span>()<span class="selector-class">.and</span>()</span><br><span class="line">            <span class="selector-class">.exceptionHandling</span>()<span class="selector-class">.authenticationEntryPoint</span>((request, response, authException) -&gt; &#123;</span><br><span class="line">                <span class="selector-tag">response</span><span class="selector-class">.sendError</span>(HttpServletResponse.SC_UNAUTHORIZED, <span class="string">"您还没有登录"</span>);</span><br><span class="line">            &#125;)<span class="selector-class">.accessDeniedHandler</span>((request, response, accessDeniedException) -&gt; &#123;</span><br><span class="line">                <span class="selector-tag">response</span><span class="selector-class">.sendError</span>(HttpServletResponse.SC_FORBIDDEN, <span class="string">"您没有访问权限"</span>);</span><br><span class="line">            &#125;)<span class="selector-class">.and</span>()</span><br><span class="line">            <span class="comment">// 基于token，所以不需要session</span></span><br><span class="line">            <span class="selector-class">.sessionManagement</span>()<span class="selector-class">.sessionCreationPolicy</span>(SessionCreationPolicy.STATELESS)</span><br><span class="line">            <span class="comment">// 允许所有用户访问</span></span><br><span class="line">            <span class="selector-class">.and</span>()<span class="selector-class">.authorizeRequests</span>()</span><br><span class="line">            <span class="selector-class">.antMatchers</span>(HttpMethod.OPTIONS)<span class="selector-class">.permitAll</span>()</span><br><span class="line">            <span class="selector-class">.antMatchers</span>(<span class="string">"/api/ping"</span>, <span class="string">"/api/captcha"</span>)<span class="selector-class">.permitAll</span>()</span><br><span class="line">            <span class="comment">// 对除了上述规则外的请求进行验证，兜底的，不能放在最前面</span></span><br><span class="line">            <span class="selector-class">.anyRequest</span>()<span class="selector-class">.authenticated</span>()</span><br><span class="line">            <span class="selector-class">.and</span>()<span class="selector-class">.addFilterAfter</span>(jwtLoginFilter, UsernamePasswordAuthenticationFilter.class)</span><br><span class="line">            <span class="selector-class">.addFilterAfter</span>(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意，主要是 <code>cors()</code>这个方法起作用。另外，我想对<code>antMatchers(HttpMethod.OPTIONS).permitAll()</code>进行下说明，前端通过POST方式访问后端的REST接口时，发现两条请求记录，一条请求的Request Method为Options，另一条请求的Reuest Method为Post。OPTIONS方法有几个作用，请参看：<a href="https://www.cnblogs.com/virtual/p/3720750.html" target="_blank" rel="noopener">https://www.cnblogs.com/virtual/p/3720750.html</a>，此处配置是为了所有的OPTIONS方法直接通过authentication。</p><h3 id="一、axios无法获取服务端返回的自定义header"><a href="#一、axios无法获取服务端返回的自定义header" class="headerlink" title="一、axios无法获取服务端返回的自定义header"></a>一、axios无法获取服务端返回的自定义header</h3><blockquote><p>为什么会有这个问题呢？也就是说我为什么要去读取reponse的header呢？前面我已经做过说明，是为了登录请求后拿到header中的token。可是打印了所有的header就是没有key为Authorization的header。</p></blockquote><h4 id="原因"><a href="#原因" class="headerlink" title="原因"></a>原因</h4><p>在使用CORS方式跨域时，浏览器只会返回以下默认头部header:</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Content-Language</span><br><span class="line">Content-<span class="keyword">Type</span></span><br><span class="line">Expires</span><br><span class="line">Last-Modified</span><br><span class="line"><span class="keyword">Pragma</span></span><br></pre></td></tr></table></figure><p>如果你想在客户端app中获取自定义的header信息，需要在服务器端header中添加Access-Control-Expose-Headers</p><h4 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h4><p>解决方法就是在上面SpringBoot对跨域支持中声明的Bean中添加一条配置，如下：</p><figure class="highlight aspectj"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> <span class="function">WebMvcConfigurer <span class="title">corsConfigurer</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> WebMvcConfigurerAdapter() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">addCorsMappings</span><span class="params">(CorsRegistry registry)</span> </span>&#123;</span><br><span class="line">                registry.addMapping(<span class="string">"/**"</span>).allowedHeaders(<span class="string">"*"</span>)</span><br><span class="line">                        .exposedHeaders(<span class="string">"Authorization"</span>)</span><br><span class="line">                        .allowedOrigins(<span class="string">"*"</span>).allowedMethods(<span class="string">"*"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>注意，<code>exposedHeaders()</code>不能使用”*“</p><p>参考：</p><ol><li><a href="http://www.uxuew.cn/vuejs/7135.html" target="_blank" rel="noopener">http://www.uxuew.cn/vuejs/7135.html</a></li><li><a href="https://www.jianshu.com/p/95b2caf7e0da" target="_blank" rel="noopener">https://www.jianshu.com/p/95b2caf7e0da</a></li></ol></div><p class="readmore"><a href="/posts/49259.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/43546.html">git 删除远程分支</a></h1><div class="post-meta">2017-10-20</div><div class="post-content"><p><a href="https://blog.zengrong.net/post/1746.html" target="_blank" rel="noopener">https://blog.zengrong.net/post/1746.html</a></p><p>删除远程分支一直报错，提示：</p><p>error: unable to delete ‘master4online’: remote ref does not exist<br>error: failed to push some refs to ‘<a href="http://code.zuihuibao.cn/zhb/car_ins.git" target="_blank" rel="noopener">http://code.zuihuibao.cn/zhb/car_ins.git</a>‘</p><p>You may need to prune your local “cache” of remote branches first. Try running:</p><p>git fetch -p origin</p><p>before deleting.</p></div><p class="readmore"><a href="/posts/43546.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/37317.html">Java规范笔记---读《阿里巴巴Java开发手册》</a></h1><div class="post-meta">2017-09-05</div><div class="post-content"><blockquote><p>以下规范摘自《阿里巴巴Java开发手册》，基本摘取了自己平时需要注意或者容易出错的部分，以提现自己。</p></blockquote><h3 id="命名风格"><a href="#命名风格" class="headerlink" title="命名风格"></a>命名风格</h3><ol><li>抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始，以 Test 结尾。</li><li>POJO 类中布尔类型的变量，都不要加 is，否则部分框架解析会引起序列化错误。</li><li><p>包名统一使用小写，点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用<strong><code>单数形式</code></strong>，但是类名如果有复数含义，类名可以使用复数形式。</p><blockquote><p>例：应用工具类包名为com.alibaba.open.util、类名为MessageUtils(此规则参考 spring 的框架结构)</p></blockquote></li><li><p>【推荐】如果使用到了设计模式，建议在类名中体现出具体模式。</p><blockquote><p>例：BeanFactory，OrderBuilder</p></blockquote></li><li>【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加)，保持代码简洁，加上有效的Javadoc注释。尽量不要在接口里定义变量，如果一定要定义变量，肯定是与接口方法相关，并且是整个应用的基础常量。</li><li>JDK8 中接口允许有默认实现，那么这个 default 方法，是对所有实现类都有价值的默 认实现。</li><li>接口实现类命名规则：<blockquote><ol><li>一般使用在接口名称后面加Impl表示实现类</li><li>如果是形容能力的接口名称，取对应的形容词做接口名(通常是–able 的形式)。例：AbstractTranslator 实现 Translatable。</li></ol></blockquote></li><li>枚举类名建议带上<code>Enum</code>后缀，枚举成员名称需要全大写，单词间用下划线隔开。<blockquote><p>枚举其实就是特殊的常量类，且构造方法被默认强制是私有。</p></blockquote></li><li>Service/DAO层方法命名规约<blockquote><ol><li>获取单个对象的方法用get做前缀。</li><li>获取多个对象的方法用list做前缀。</li><li>获取统计值的方法用count做前缀。</li><li>插入的方法用save做前缀。</li><li>删除的方法用remove做前缀。</li><li>修改的方法用update做前缀。</li></ol></blockquote></li><li>领域模型命名规约<blockquote><ol><li>数据对象:xxxDO，xxx即为数据表名。</li><li>数据传输对象:xxxDTO，xxx为业务领域相关的名称。</li><li>展示对象:xxxVO，xxx一般为网页名称。</li></ol></blockquote></li></ol><h3 id="常亮定义"><a href="#常亮定义" class="headerlink" title="常亮定义"></a>常亮定义</h3><ol><li>常量的复用层次<blockquote><ol><li>跨应用共享常量:放置在二方库中，通常是client.jar中的constant目录下。</li><li>应用内共享常量:放置在一方库的modules中的constant目录下。</li><li>子工程内部共享常量:即在当前子工程的constant目录下。</li><li>包内共享常量:即在当前包下单独的constant目录下。</li><li>类内共享常量:直接在类内部private static final定义。</li></ol></blockquote></li></ol><h3 id="代码格式"><a href="#代码格式" class="headerlink" title="代码格式"></a>代码格式</h3><ol><li>如果是大括号内为空，则简洁地写成{}即可，不需要换行，右大括号后还有else等代码则不换行；<strong>表示终止的右大括号后必须换行</strong>。</li><li>if/for/while/switch/do 等保留字与括号之间都必须加空格。</li><li>任何二目、三目运算符的左右两边都需要加一个空格。</li><li>缩进采用 4 个空格，禁止使用 tab 字符。</li><li>【推荐】方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义<br>之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。</li></ol><h3 id="OOP规约"><a href="#OOP规约" class="headerlink" title="OOP规约"></a>OOP规约</h3><ol><li>【强制】所有的覆写方法，必须加@Override 注解。</li><li><p>【强制】所有的相同类型的包装类对象之间值的比较，全部使用 equals 方法比较。</p><blockquote><p>说明:对于 Integer var = ? 在-128 至 127 范围内的赋值，Integer 对象是在 IntegerCache.cache 产生，会复用已有对象，这个区间内的 Integer 值可以直接使用==进行 判断，但是这个区间之外的所有数据，都会在堆上产生，并不会复用已有对象，这是一个大坑， 推荐使用 equals 方法进行判断。</p></blockquote></li><li><p>基本数据类型与包装数据类型</p><blockquote><ol><li>【强制】所有的POJO类属性必须使用包装数据类型。</li><li>【强制】RPC方法的返回值和参数必须使用包装数据类型。</li><li>【推荐】所有的局部变量使用基本数据类型。</li></ol></blockquote></li><li>【强制】定义 DO/DTO/VO 等 POJO 类时，不要设定任何属性默认值。</li><li>【强制】构造方法里面禁止加入任何业务逻辑，如果有初始化逻辑，请放在 init 方法中。</li><li><p>【强制】POJO 类必须写 toString 方法。</p><blockquote><p>使用 IDE 的中工具:source&gt; generate toString 时，如果继承了另一个 POJO 类，注意在前面加一下 super.toString。 说明:在方法执行抛出异常时，可以直接调用 POJO 的 toString()方法打印其属性值，便于排 查问题。</p></blockquote></li><li><p>【推荐】 类内方法定义顺序依次是:公有方法或保护方法 &gt; 私有方法 &gt; getter/setter 方法。</p></li><li>【推荐】 类内方法定义顺序依次是:公有方法或保护方法 &gt; 私有方法 &gt; getter/setter 方法。当一个类有多个构造方法，或者多个同名方法，这些方法应该按顺序放置在一起， 便于阅读。</li><li>【推荐】setter 方法中，参数名称与类成员变量名称一致，this.成员名 = 参数名。在<br>getter/setter 方法中，不要增加业务逻辑，增加排查问题的难度。</li><li>【强制】关于 hashCode 和 equals 的处理，遵循如下规则:<blockquote><ol><li>只要重写equals，就必须重写hashCode。</li><li>因为Set存储的是不重复的对象，依据hashCode和equals进行判断，所以Set存储的 对象必须重写这两个方法。</li><li>如果自定义对象做为Map的键，那么必须重写hashCode和equals。<br>说明: <code>String</code>重写了<code>hashCode</code>和<code>equals</code>方法，所以我们可以使用<code>String</code>对象作为key来使用。</li></ol></blockquote></li><li>【强制】ArrayList的subList结果不可强转成ArrayList，否则会抛出ClassCastException 异常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ; 说明:subList 返回的是 ArrayList 的内部类 SubList，并不是 ArrayList ，而是 ArrayList 的一个视图，对于SubList子列表的所有操作最终会反映到原列表上。</li><li><p>【强制】使用集合转数组的方法，必须使用集合的 toArray(T[] array)，传入的是类型完全 一样的数组，大小就是 list.size()。</p><blockquote><p>说明:使用 toArray 带参方法，入参分配的数组空间不够大时，toArray 方法内部将重新分配 内存空间，并返回新数组地址;如果数组元素大于实际所需，下标为[ list.size() ]的数组 元素将被置为 null，其它数组元素保持原值，因此最好将方法入参数组大小定义与集合元素 个数一致。</p><ul><li>正例:</li></ul></blockquote><pre><code><figure class="highlight smali"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">		List&lt;String&gt; list =<span class="built_in"> new </span>ArrayList&lt;String&gt;(2); </span><br><span class="line">		list.add(<span class="string">"guan"</span>);		list.add(<span class="string">"bao"</span>);		String[]<span class="built_in"> array </span>=<span class="built_in"> new </span>String[list.size()]; </span><br><span class="line">	<span class="built_in">	array </span>= list.toArray(array);</span><br><span class="line">		```	</span><br><span class="line">	* 反例:直接使用 toArray 无参方法存在问题，此方法返回值只能是 Object[]类，若强转其它 类型数组将出现 ClassCastException 错误。13. 【强制】使用工具类 Arrays.asList()把数组转换成集合时，不能使用其修改集合相关的方 法，它的<span class="built_in"> add/remove/clear </span>方法会抛出 UnsupportedOperationException 异常。 </span><br><span class="line">	&gt; 说明:asList 的返回对象是一个 Arrays 内部类，并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式，只是转换接口，后台的数据仍是数组。</span><br><span class="line">14. remove 元素请使用`Iterator`方式，如果并发操作，需要对 Iterator 对象加锁。</span><br><span class="line">15. 【推荐】集合初始化时，指定集合初始值大小。	&gt; 说明:HashMap使用HashMap(int initialCapacity) 初始化，</span><br><span class="line">		&gt; 1. 正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75，如果暂时无法确定初始值大小，请设置为 16。</span><br><span class="line">		&gt; 2. 反例:HashMap 需要放置 1024 个元素，由于没有设置容量初始大小，随着元素不断增加，容 量 7 次被迫扩大，resize 需要重建 hash 表，严重影响性能。</span><br><span class="line">16. 【推荐】使用 entrySet 遍历 Map 类集合 KV，而不是 keySet 方式进行遍历。如果是 JDK8，使用 Map.foreach 方法。</span><br><span class="line">17. 【【强制】SimpleDateFormat 是线程不安全的类，一般不要定义为static变量，如果定义为static，必须加锁，或者使用 DateUtils 工具类。	&gt; 正例:注意线程安全，使用 DateUtils。亦推荐如下处理:</span><br></pre></td></tr></table></figure></code></pre></li></ol></div><p class="readmore"><a href="/posts/37317.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/56838.html">Java获取泛型的真实类型</a></h1><div class="post-meta">2017-08-22</div><div class="post-content"><p><a href="https://github.com/FasterXML/java-classmate" target="_blank" rel="noopener">https://github.com/FasterXML/java-classmate</a></p><p><a href="http://www.cnblogs.com/JamKong/p/4540142.html" target="_blank" rel="noopener">http://www.cnblogs.com/JamKong/p/4540142.html</a></p></div><p class="readmore"><a href="/posts/56838.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/6407.html">RabbitMQ从入门到精通—publish confirm机制</a></h1><div class="post-meta">2017-08-20</div><div class="post-content"><blockquote><p>前面我们说到防止消息丢失的ACK机制，这只是针对消费者和RabbitMQ Server的，但是生产者呢？生产者如何确认消息发送成功？其实最近我们负责和客户端通信的PHP端经常就会问我：“消息你们到底收没收到啊？”，因为他自己也不确定到底有没有发送成功，这就需要今天所说的<code>publish confirm</code>机制。</p></blockquote></div><p class="readmore"><a href="/posts/6407.html">Leer más</a></p></div><nav class="page-navigator"><span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="page-number" href="/page/3/">3</a><span class="space">&hellip;</span><a class="page-number" href="/page/5/">5</a><a class="extend next" rel="next" href="/page/2/">Siguiente</a></nav></div></div><div class="pure-u-1-4 hidden_mid_and_down"><div id="sidebar"><div class="widget"><form class="search-form" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank"><input type="text" name="q" maxlength="20" placeholder="Search"><input type="hidden" name="sitesearch" value="https://www.dev-heaven.com"></form></div><div class="widget"><div class="widget-title"><i class="fa fa-folder-o">Categorías</i></div><ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/博客/">博客</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/生活/">生活</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/随笔/">随笔</a></li></ul></div><div class="widget"><div class="widget-title"><i class="fa fa-star-o">Etiquetas</i></div><div class="tagcloud"><a href="/tags/springMVC/" style="font-size:15px">springMVC</a> <a href="/tags/docker/" style="font-size:15px">docker</a> <a href="/tags/express/" style="font-size:15px">express</a> <a href="/tags/POI/" style="font-size:15px">POI</a> <a href="/tags/Java/" style="font-size:15px">Java</a> <a href="/tags/Linux/" style="font-size:15px">Linux</a> <a href="/tags/firewall/" style="font-size:15px">firewall</a> <a href="/tags/MySQL/" style="font-size:15px">MySQL</a> <a href="/tags/mybatis/" style="font-size:15px">mybatis</a> <a href="/tags/Ngrok/" style="font-size:15px">Ngrok</a> <a href="/tags/微信/" style="font-size:15px">微信</a> <a href="/tags/ECS/" style="font-size:15px">ECS</a> <a href="/tags/RabbitMQ/" style="font-size:15px">RabbitMQ</a> <a href="/tags/Nginx/" style="font-size:15px">Nginx</a> <a href="/tags/域名/" style="font-size:15px">域名</a> <a href="/tags/Nodejs/" style="font-size:15px">Nodejs</a> <a href="/tags/nvm/" style="font-size:15px">nvm</a> <a href="/tags/SpringBoot/" style="font-size:15px">SpringBoot</a> <a href="/tags/log4j2/" style="font-size:15px">log4j2</a> <a href="/tags/Docker/" style="font-size:15px">Docker</a> <a href="/tags/x-www-form-urlencoded/" style="font-size:15px">x-www-form-urlencoded</a> <a href="/tags/form-data/" style="font-size:15px">form-data</a> <a href="/tags/axios/" style="font-size:15px">axios</a> <a href="/tags/css/" style="font-size:15px">css</a> <a href="/tags/git/" style="font-size:15px">git</a> <a href="/tags/life/" style="font-size:15px">life</a> <a href="/tags/health/" style="font-size:15px">health</a> <a href="/tags/npm/" style="font-size:15px">npm</a> <a href="/tags/nodejs/" style="font-size:15px">nodejs</a> <a href="/tags/nrm/" style="font-size:15px">nrm</a> <a href="/tags/redis/" style="font-size:15px">redis</a> <a href="/tags/Spring/" style="font-size:15px">Spring</a> <a href="/tags/spring-data-redis/" style="font-size:15px">spring-data-redis</a> <a href="/tags/pyenv/" style="font-size:15px">pyenv</a> <a href="/tags/Python/" style="font-size:15px">Python</a> <a href="/tags/ReentrantLock/" style="font-size:15px">ReentrantLock</a> <a href="/tags/https/" style="font-size:15px">https</a> <a href="/tags/poet/" style="font-size:15px">poet</a> <a href="/tags/shell/" style="font-size:15px">shell</a></div></div><div class="widget"><div class="widget-title"><i class="fa fa-file-o">Recientes</i></div><ul class="post-list"><li class="post-list-item"><a class="post-list-link" href="/posts/48761.html">关于ReentrantLock实现的一点思考</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/61735.html">Java内存优化之POI Excel（三）</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/64862.html">Java内存优化之POI Excel（二）</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/27940.html">Java内存优化之POI Excel（一）</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/955.html">最佳实践之Java基础</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/49259.html">Vue + SpringBoot 小问题记录</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/43546.html">git 删除远程分支</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/37317.html">Java规范笔记---读《阿里巴巴Java开发手册》</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/56838.html">Java获取泛型的真实类型</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/6407.html">RabbitMQ从入门到精通—publish confirm机制</a></li></ul></div><div class="widget"><div class="widget-title"><i class="fa fa-external-link">Blogroll</i></div><ul></ul><a href="http://www.example1.com/" title="site-name1" target="_blank">site-name1</a><ul></ul><a href="http://www.example2.com/" title="site-name2" target="_blank">site-name2</a><ul></ul><a href="http://www.example3.com/" title="site-name3" target="_blank">site-name3</a></div></div></div><div class="pure-u-1 pure-u-md-3-4"><div id="footer">Copyright © 2019 <a href="/." rel="nofollow">菜鸟IT路.</a> Powered by<a rel="nofollow" target="_blank" href="https://hexo.io"> Hexo.</a><a rel="nofollow" target="_blank" href="https://github.com/tufu9441/maupassant-hexo"> Theme</a> by<a rel="nofollow" target="_blank" href="https://github.com/pagecho"> Cho.</a></div></div></div><a class="show" id="rocket" href="#top"></a><script type="text/javascript" src="/js/totop.js?v=0.0.0" async></script><script type="text/javascript" src="//lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js" async></script><script type="text/javascript" src="/js/fancybox.js?v=0.0.0" async></script><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css"><script type="text/javascript" src="/js/codeblock-resizer.js?v=0.0.0"></script><script type="text/javascript" src="/js/smartresize.js?v=0.0.0"></script></div></body></html>